#include "inc_fan.h"

static int16_t temp = INT16_MIN;

static uint8_t mode = 0;

static uint16_t range = 0;

static uint8_t shakeHead = 0;

// 实际可用数值为 1~NUMBER_OF_GEARS，超出部分会被 SetGear 截断，本变量只能用于读取，写入只能通过 SetGear
static int8_t gear = 0;

static uint8_t mustShowGear = 0;

static char uart2RxBuffer[1] = {0};

void Print_Temp() {
    // 当 temp 第一次产生可用值后才重绘温度
    static int16_t tmpTemp = INT16_MIN;
    static char str[4];

    if (tmpTemp == temp) return;

    tmpTemp = temp;

    const uint8_t sub[] = {0, 1, 2};

    itoa(temp, str, 10);
    const size_t len = strlen(str);

    OLED_ShowChinese(2, 1, sub, 2);
    OLED_ShowString(2, 5, str);
    OLED_ShowChinese(2, 5 + len, sub + 2, 1);
    OLED_Clear_Part(2, 7 + len, 7 + len);
}

void Print_Mode() {
    static uint8_t tmpMode = UINT8_MAX;

    // 档位未发生变化则不处理
    if (tmpMode == mode) return;

    tmpMode = mode;

    // 关机
    if (mode == 0) {
        const uint8_t sub[] = {3, 4, 5};
        OLED_ShowChinese(1, 1, sub, 3);

        OLED_Clear_Part(1, 7, 16);
        OLED_Clear_Part(3, 1, 16);
        return;
    }

    if (mode == 1) {
        // 标准模式
        const char sub[] = {8, 9, 10, 11};
        OLED_ShowChinese(1, 1, (const uint8_t *) sub, 4);
    } else {
        // 智能模式
        const char sub[] = {6, 7, 10, 11};
        OLED_ShowChinese(1, 1, (const uint8_t *) sub, 4);
    }
}

void Print_Gear() {
    static int8_t tmpGear = DEFAULT_GEAR;

    // 当档位发生变化或者特殊情况下展示档位
    if (mustShowGear || tmpGear != gear) {
        const uint8_t sub[] = {14};

        OLED_ShowNum(3, 1, gear, 2);
        OLED_ShowChinese(3, 3, sub, 1);

        tmpGear = gear;
        mustShowGear = 0;
    }
}

void Set_Mode(const uint8_t newMode) {
    mode = newMode;

    if (mode == 0) {
        // 关机
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
        __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_2, 0);
    } else {
        // 其他模式都需要电机工作
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
        __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_2, 100);
    }
}

// 实际可用数值为 1~NUMBER_OF_GEARS，超出部分会产生截断
void Set_Gear(int8_t newGear) {
    static uint32_t pwm = 0;

    // 限制档位的范围
    if (newGear < 1) {
        newGear = 1;
    } else if (newGear > NUMBER_OF_GEARS) {
        newGear = NUMBER_OF_GEARS;
    }

    const uint32_t autoReload = __HAL_TIM_GetAutoreload(&htim3);
    const uint32_t gears = autoReload / NUMBER_OF_GEARS;

    pwm = gears * newGear;
    gear = newGear;

    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwm);
}

void Set_Shake_Head(const uint8_t flag) {
    shakeHead = flag;

    if (shakeHead == 0) {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
    } else {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
    }
}

void Buz_Sounds() {
    // 蜂鸣器发声
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
}

// 本函数放入 SysTick 中断中，确保及时关闭蜂鸣器
void Fan_Buz_Close() {
    // Proteus 仿真中，部分变量如果在声明时初始化，初始化会失败，在这里可能导致第一次蜂鸣声非常短
    static uint32_t startTick;

    if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_SET) {
        startTick = 0;
        return;
    }

    if (startTick == 0) {
        startTick = HAL_GetTick();
        return;
    }

    // 蜂鸣器至少响 100 ms
    if (HAL_GetTick() - startTick >= 100) {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    }
}

void Auto_Set_Temp() {
    // 每 750 ms 才检查一次温度
    if (HAL_GetTick() % 750 != 0) return;

    DS18B20_Init();
    temp = DS18B20_GetTemp_SkipRom();
}

void Auto_Set_Gear() {
    static int16_t tmpTemp = -1;
    static int8_t tmpGear = DEFAULT_GEAR;

    // 处于智能模式，并且是第一次启动智能模式或者温度/档位发生变化，才会自动切换档位
    if (mode != 2 || (tmpTemp == temp && tmpGear == gear)) return;

    tmpTemp = temp;
    tmpGear = tmpTemp / 2 - 8;

    // 避免中断切换了模式后，还以为目前是智能模式
    __disable_irq();
    if (mode == 2) {
        Set_Gear(tmpGear);
        // 实际配置的档位不一定是计算出来的档位，因为档位存在范围限制
        tmpGear = gear;
    }
    __enable_irq();
}

void Fan_Start_Ranging() {
    if (HAL_GetTick() % 500 != 0) return;

    // Proteus 8.15 中 STM32F103 的 HAL 库切换引脚电平至少得 10us，因此这样就相当于发送至少 10us 的 TTL
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
}

void Ranging(const GPIO_PinState state) {
    static uint32_t startTick = 0;

    if (mode == 0) return;

    if (state == GPIO_PIN_SET) {
        // 如果是上升沿触发的中断
        startTick = GetTick_Us();
    } else {
        // 如果是下降沿触发的中断
        range = (GetTick_Us() - startTick) / 2 * 0.0345;
        if (range < 300) {
            if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_2) == GPIO_PIN_RESET) {
                HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
            }
        } else {
            Set_Shake_Head(shakeHead);
        }
    }
}

void Power_Switch() {
    // 关机时保留之前的工作模式
    static uint8_t lastMode = DEFAULT_MODE;

    if (mode == 0) {
        Set_Mode(lastMode);
        // 开机必须展示档位
        mustShowGear = 1;
    } else {
        lastMode = mode;
        Set_Mode(0);
        // 关机需撤销必须展示档位（防止用户连续点击导致关机后还显示档位）
        mustShowGear = 0;
    }
}

void Mode_Switch() {
    // 模式切换时保留原档位
    static int8_t lastGear = DEFAULT_GEAR;

    // 关机状态下无法切换模式
    if (mode == 0) return;

    if (mode == 1) {
        lastGear = gear;
        Set_Mode(2);
    } else {
        // 切换回标准模式并还原档位
        Set_Mode(1);
        Set_Gear(lastGear);
    }
}

void Gear_Add() {
    if (mode != 1) return;

    Set_Gear(gear + 1);
}

void Gear_Reduce() {
    if (mode != 1) return;

    Set_Gear(gear - 1);
}

void Fan_Receive_Control() {
    HAL_UART_Receive_IT(&huart2, (uint8_t *) uart2RxBuffer, 1);
}

void Fan_Execute_Control() {
    // 遥控指令接收成功后执行指令
    switch (uart2RxBuffer[0]) {
        case '0':
            Fan_Button(GPIO_PIN_9);
            break;
        case '1':
            Fan_Button(GPIO_PIN_10);
            break;
        case '2':
            Fan_Button(GPIO_PIN_11);
            break;
        case '3':
            Fan_Button(GPIO_PIN_12);
            break;
        default:
            break;
    }
}

void Fan_Setup() {
    // 第一次获取到的温度是不准的，舍弃
    DS18B20_Init();
    DS18B20_GetTemp_SkipRom();
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
    Fan_Receive_Control();
    Set_Gear(DEFAULT_GEAR);
    OLED_Init();
}

void Fan_Loop() {
    Auto_Set_Temp();
    Auto_Set_Gear();
    Print_Mode();
    Print_Temp();
    Print_Gear();
}

void Fan_Button(const uint16_t GPIO_Pin) {
    const char *msg;

    switch (GPIO_Pin) {
        case GPIO_PIN_3:
            Ranging(HAL_GPIO_ReadPin(GPIOB, GPIO_Pin));
            return;
        case GPIO_PIN_9:
            msg = "Power switch\r\n";
            Power_Switch();
            break;
        case GPIO_PIN_10:
            msg = "Mode switch\r\n";
            Mode_Switch();
            break;
        case GPIO_PIN_11: {
            msg = "Gear add\r\n";
            Gear_Add();
        }
        break;
        case GPIO_PIN_12: {
            msg = "Gear reduce\r\n";
            Gear_Reduce();
        }
        break;
        default:
            msg = "Unknow button\r\n";
            break;
    }

    // 蜂鸣器发声
    Buz_Sounds();
    // 串口显示用户按下的按钮
    HAL_UART_Transmit_DMA(&huart1, (const uint8_t *) msg, strlen(msg));
}
